#include <Arduino.h>
#include <TFT_eSPI.h> // Hardware-specific library
#include <SPI.h>
#include <WiFi.h>
#include <Adafruit_AHTX0.h>
#include <ArduinoJson.h>
#include "BMP.h"

#include <HTTPClient.h>  //http连接
#include <ArduinoJson.h> //引入json
//异步调用
#include <AsyncTCP.h>
#include <AsyncTimer.h>

#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"
#define WIDTH 160
#define HEIGHT 80

#define REDLED1 12 // WIFI开始连接，亮灯，连接成功后，灯灭
#define REDLED2 13 // WIFI开始连接，亮灯，连接成功后，灯灭

HTTPClient http;

//异步调用参数
AsyncTimer t;
String header;
enum ParseState
{
    PARSE_REQ_START,
    PARSE_REQ_HEADERS,
    PARSE_REQ_BODY,
    PARSE_REQ_END,
    PARSE_REQ_FAIL,
    PARSE_REQ_PENDING
};
ParseState _parseState;
// 待显示图片尺寸
uint16_t imageSize;
// 已经下载的实体尺寸
uint16_t readBytesSize = -1;
// 图片缓存, 限制大小为16KB，你也可以设置成其他尺寸。
uint8_t *imageBuffer = (uint8_t *)heap_caps_malloc(16 * 1024, MALLOC_CAP_8BIT);
// 两张sprite的偏移
int16_t headerSpriteOffset = 0;

String payload_time = "aaaaaaaaaaaaaaaaaa";
String http_status = "";

TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
TFT_eSprite spr = TFT_eSprite(&tft);
WiFiClient client;

// =========================================================================
// Setup
// =========================================================================
const char *ssid = "CMCC-1007"; // WIFI账户
// const char *ssid = "TGYiPhone";
const char *password = "18851850631"; // WIFI密码
// const char *password = "1234567809"; // WIFI密码
const char *host = "192.168.0.0"; //自己PC的ip地址

int16_t gpu_usage;
int16_t cpu_usage;
int16_t ram_usage;
int i = 1;

/**
 * @brief 从初次原始报文中提取Content-Length
 * @param responseBody 原始报文
 * @param bodyBeginIndex body起始索引
 * @return 无
 **/
uint16_t extractContentLengthFromResponseRaw(String &responseBody,
                                             uint16_t &bodyBeginIndex)
{
    int index = responseBody.indexOf("Content-Length:");
    int eIndex = responseBody.indexOf('\n', index);
    String value = responseBody.substring(index + 16, eIndex);
    bodyBeginIndex = responseBody.indexOf("\r\n\r\n") + 4;
    return atoi(value.c_str());
}
/**
 * @brief 深度复制bytes
 * @param buffer 目标的容器
 * @param source 原始内容
 * @param length 数据长度
 * @param SrcOffset 原始内容的读取偏移
 * @param bufferOffset 目标的容器的写入偏移
 * @return 处理的数据长度
 **/
size_t copyBytes(uint8_t *source, uint8_t *buffer, size_t length,
                 size_t SrcOffset, size_t bufferOffset)
{
    size_t count = 0;
    while (count + SrcOffset < length)
    {
        *(buffer + count + bufferOffset) = source[count + SrcOffset];
        count++;
    }
    return count;
}

/**
 * @brief 从初次原始报文中提取Content-Length
 * @param frameData 原始报文
 * @param bodyData body数据容器
 * @param length body数据长度
 * @param offset body数据起始索引
 * @return 处理的body长度
 **/
uint16_t extractBodyChunkFromResponseRaw(uint8_t *frameData, uint8_t *bodyData,
                                         uint16_t length, uint16_t srcOffset,
                                         size_t bufferOffset)
{
    copyBytes(frameData, bodyData, length, srcOffset, bufferOffset);
    return length - srcOffset;
}
//异步网络请求
void asyncReqeust()
{
    static AsyncClient *aClient;
    http_status = "???????====??????";
    if (aClient)
    {
        // aClient already exists
        // aClient 繁忙
        http_status = "busying====";
        return;
    }
    http_status = "resting!!!!!!!!!!!";
    aClient = new AsyncClient();
    if (!aClient)
    {
        // could not allocate aClient
        // aClient 无法创建
        return;
    }
    http_status = "aClient success";
    aClient->onError(
        [&](void *arg, AsyncClient *client, int error)
        {
            Serial.println("getFollowersAsync:Connect Error");
            aClient = NULL;
            delete client;
        },
        NULL);

    aClient->onConnect(
        [&](void *arg, AsyncClient *client)
        {
            Serial.println("getFollowersAsync:Connected");
            aClient->onError(NULL, NULL);

            // 连接断开的动作
            client->onDisconnect(
                [&](void *arg, AsyncClient *c)
                {
                    // finish = true;
                    aClient = NULL;
                    delete c;
                    Serial.println("getFollowersAsync:Disconnected");
                },
                NULL);

            // 接收到服务器数据时候的动作
            client->onData(
                [&](void *arg, AsyncClient *c, void *data, size_t len)
                {
                    http_status = "dataing";
                    uint8_t *d = (uint8_t *)data;
                    // _parseState为解析状态枚举类型
                    if (_parseState == PARSE_REQ_START)
                    {
                        String content = String((char *)data);
                        int length = content.length();

                        _parseState = PARSE_REQ_HEADERS;
                        uint16_t bodyBeginIndex = 0;
                        // 从响应头中获取长度
                        imageSize = extractContentLengthFromResponseRaw(content,
                                                                        bodyBeginIndex);
                        Serial.printf("Content-Length:%d\n", imageSize);
                        // payload_time = content.substring(length - imageSize, imageSize);
                        payload_time = content.substring(40, 48);
                        header = content.substring(0, bodyBeginIndex);
                    }
                    _parseState = PARSE_REQ_START;
                    //处理完毕数据以后把连接删除
                    //c = NULL;
                    //aClient = NULL;
                },
                NULL);

            // send the request
            // 发送请求
            client->write(
                "GET /checktime?otype=json HTTP/1.1\r\n"
                "Host: vv.video.qq.com\r\n"
                "Accept: */*\r\n"
                "Accept-Encoding: gzip, deflate, br"
                "Connection: keep-alive"
                "User-Agent: PostmanRuntime/7.29.2\r\n\r\n");
        },
        NULL);
    if (!aClient->connect("vv.video.qq.com", 80))
    {
        Serial.println("Connect Fail");
        http_status = "Connect Fail";
        AsyncClient *client = aClient;
        aClient = NULL;
        delete client;
    }
}

void show()
{

    struct tm timeinfo;
    if (!getLocalTime(&timeinfo))
    {
        //如果获取失败，就开启网络对时
        Serial.println("Failed to obtain time");
        configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
        return;
    }

    spr.drawRect(0, 0, WIDTH, HEIGHT, TFT_BLACK);
    spr.fillSprite(TFT_BLACK);
    spr.setTextColor(TFT_WHITE, TFT_BLACK);
    spr.setTextFont(1);
    spr.setTextSize(2);
    spr.setCursor(0, 0);
    spr.println(http_status);
    spr.println(&timeinfo, "%T");
    spr.println(payload_time);
    spr.pushSprite(0, 0);
}

void shift_str()
{

    struct tm timeinfo;
    if (!getLocalTime(&timeinfo))
    {
        //如果获取失败，就开启网络对时
        Serial.println("Failed to obtain time");
        configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
        return;
    }
    // digitalWrite(REDLED1, !digitalRead(REDLED1));
    // digitalWrite(REDLED2, !digitalRead(REDLED2));
    payload_time = timeinfo.tm_sec;
}

void shift()
{

    digitalWrite(REDLED1, !digitalRead(REDLED1));
    digitalWrite(REDLED2, !digitalRead(REDLED2));
}

void setup()
{
    Serial.begin(115200);
    pinMode(REDLED1, OUTPUT);
    pinMode(REDLED2, OUTPUT);

    tft.init();
    tft.setRotation(3);
    tft.setTextFont(2);

    // Create a sprite of defined size
    spr.createSprite(WIDTH, HEIGHT);

    // Clear the TFT screen to black
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK);
    // tft.setTextSize(1);
    tft.setCursor(25, 12);
    tft.println("Power by");
    tft.setCursor(25, 28);
    tft.setTextSize(2);
    tft.println("Arduino");
    WiFi.begin(ssid, password);
    Serial.println();
    Serial.print("WIFI Connecting");
    tft.fillScreen(TFT_BLACK); // LCD清空
    tft.setCursor(0, 0);
    tft.setTextSize(1);
    tft.print("WIFI Connecting");

    while (WiFi.status() != WL_CONNECTED)
    {
        digitalWrite(REDLED1, 1);
        delay(500);
        Serial.print(".");
        tft.print(".");
        digitalWrite(REDLED1, 0);
    }
    // 从网络时间服务器上获取并设置时间
    configTime(8 * 3600, 0, NTP1, NTP2, NTP3);
    tft.println();
    tft.println("WiFi connected!");
    // asyncReqeust();
    tft.fillScreen(TFT_BLACK); // LCD清空
    delay(500);
    tft.fillScreen(TFT_BLACK); // LCD清空
    tft.setCursor(4, 20);
    tft.setTextSize(2);
    t.setInterval(shift_str, 610000);
    t.setInterval(shift, 500);
    // 5s调用一次http请求
    t.setInterval(asyncReqeust, 10000);
    tft.println("Ready!!! GO");
    tft.fillScreen(TFT_BLACK);
    digitalWrite(REDLED1, !digitalRead(REDLED1));
    // show();
}

void loop()
{

    show();
    t.handle();
}
